/**
 * @Project Name:hoofungson-elk-log
 * @Package Name:cn.hoofungson.framework.elk.log.monitor
 * @Since JDK 1.8
 */

package cn.hoofungson.framework.elk.log.monitor;

import cn.hoofungson.framework.elk.log.constant.config.LogFieldConstants;
import cn.hoofungson.framework.elk.log.constant.config.MonitorLogFieldConstants;
import cn.hoofungson.framework.elk.log.constant.json.JsonConstants;
import cn.hoofungson.framework.elk.log.exception.ServiceErrorCode;
import cn.hoofungson.framework.elk.log.util.AppInfoUtils;
import cn.hoofungson.framework.elk.log.util.StringBufferUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import org.springframework.util.CollectionUtils;

import java.util.Map;

/**
 * @Description: 监控日志
 * @Author 胡松 hoofungson@163.com
 * @Date 2019-08-27 10:23
 * @Version V1.0
 */
public class MonitorLogger {

    private final Logger logger;
    public static final Marker MONITOR_EVENT_MARKER = MarkerFactory.getMarker("MONITOR_EVENT");
    private static final Logger log = LoggerFactory.getLogger(MonitorLogger.class);

    protected MonitorLogger(Logger logger) {
        this.logger = logger;
    }

    /**
     * 属性追加双引号处理
     *
     * @param string
     * @return
     **/
    public static String quote(String string) {
        if (string != null && string.length() != 0) {
            boolean c = false;
            int len = string.length();
            StringBuffer sb = new StringBuffer(len + 4);
            sb.append('\"');

            for (int i = 0; i < len; ++i) {
                char var6 = string.charAt(i);
                switch (var6) {
                    case '\b':
                        sb.append("\\b");
                        break;
                    case '\t':
                        sb.append("\\t");
                        break;
                    case '\n':
                        sb.append("\\n");
                        break;
                    case '\f':
                        sb.append("\\f");
                        break;
                    case '\r':
                        sb.append("\\r");
                        break;
                    case '\"':
                    case '\\':
                        sb.append('\\');
                        sb.append(var6);
                        break;
                    case '/':
                        sb.append('\\');
                        sb.append(var6);
                        break;
                    default:
                        if (var6 < 32) {
                            String t = "000" + Integer.toHexString(var6);
                            sb.append("\\u").append(t.substring(t.length() - 4));
                        } else {
                            sb.append(var6);
                        }
                }
            }

            sb.append('\"');
            return sb.toString();
        } else {
            return "\"\"";
        }
    }

    /**
     * 转换为JSON
     *
     * @param monitorCode
     * @param message
     * @param context
     * @return
     **/
    public static String toJson(String monitorCode, String message, Map<String, String> context) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(JsonConstants.JSON_START_SYMBOL);
        if (StringUtils.isNotBlank(monitorCode)) {
            if (StringBufferUtils.isNotBlank(stringBuffer)
                    && !StringBufferUtils.isEndWith(stringBuffer, JsonConstants.JSON_START_SYMBOL)) {
                stringBuffer.append(",");
            }
            stringBuffer.append("\"");
            stringBuffer.append(MonitorLogFieldConstants.MONITOR_CODE);
            stringBuffer.append("\":");
            stringBuffer.append("\"");
            stringBuffer.append(monitorCode);
            stringBuffer.append("\"");
        }
        if (StringUtils.isNotBlank(message)) {
            if (StringBufferUtils.isNotBlank(stringBuffer)
                    && !StringBufferUtils.isEndWith(stringBuffer, JsonConstants.JSON_START_SYMBOL)) {
                stringBuffer.append(",");
            }
            stringBuffer.append("\"");
            stringBuffer.append(LogFieldConstants.MESSAGE);
            stringBuffer.append("\":");
            stringBuffer.append("\"");
            stringBuffer.append(message);
            stringBuffer.append("\"");
        }
        if (CollectionUtils.isEmpty(context)) {
            if (StringBufferUtils.isNotBlank(stringBuffer)
                    && !StringBufferUtils.isEndWith(stringBuffer, JsonConstants.JSON_START_SYMBOL)) {
                stringBuffer.append(",");
            }
            stringBuffer.append("\"");
            stringBuffer.append(MonitorLogFieldConstants.CONTEXT_CONTENT);
            stringBuffer.append("\":");
            stringBuffer.append("\"\"");
        }
        if (!CollectionUtils.isEmpty(context)) {
            if (StringBufferUtils.isNotBlank(stringBuffer)
                    && !StringBufferUtils.isEndWith(stringBuffer, JsonConstants.JSON_START_SYMBOL)) {
                stringBuffer.append(",");
            }
            stringBuffer.append("\"");
            stringBuffer.append(MonitorLogFieldConstants.CONTEXT_CONTENT);
            stringBuffer.append("\":");
            stringBuffer.append("\"" + context.toString() + "\"");
        }
        stringBuffer.append(JsonConstants.JSON_END_SYMBOL);
        return stringBuffer.toString();
    }

    private void selfError(String monitorCode, String oriMessage, Map<String, String> context, Throwable throwable) {
        log.error(MONITOR_EVENT_MARKER, "{\"{}\":\"{}\",\"{}\":\"打印监控日志的时候出错\"}", new Object[]{MonitorLogFieldConstants.MONITOR_CODE, monitorCode, LogFieldConstants.MESSAGE, throwable});
    }

    public boolean isInfoEnabled() {
        return this.logger.isInfoEnabled(MONITOR_EVENT_MARKER);
    }

    public void info(String monitorCode, String message) {
        this.info(monitorCode, message, (Map) null, (Throwable) null);
    }

    public void info(String monitorCode, String message, Throwable throwable) {
        this.info(monitorCode, message, (Map) null, throwable);
    }

    public void info(String monitorCode, String message, Map<String, String> context) {
        this.info(monitorCode, message, context, (Throwable) null);
    }

    public void info(String monitorCode, String message, Map<String, String> context, Throwable throwable) {
        if (this.isInfoEnabled()) {
            monitorCode = this.resolveMonitorCode(monitorCode, "I");
            try {
                this.logger.info(MONITOR_EVENT_MARKER, toJson(monitorCode, message, context), throwable);
            } catch (Throwable var6) {
                this.selfError(monitorCode, message, context, throwable);
            }
        }

    }

    public boolean isWarnEnabled() {
        return this.logger.isWarnEnabled(MONITOR_EVENT_MARKER);
    }

    public void warn(String monitorCode, String message) {
        this.warn(monitorCode, message, (Map) null, (Throwable) null);
    }

    public void warn(String monitorCode, String message, Throwable throwable) {
        this.warn(monitorCode, message, (Map) null, throwable);
    }

    public void warn(String monitorCode, String message, Map<String, String> context) {
        this.warn(monitorCode, message, context, (Throwable) null);
    }

    public void warn(String monitorCode, String message, Map<String, String> context, Throwable throwable) {
        if (this.isWarnEnabled()) {
            monitorCode = this.resolveMonitorCode(monitorCode, "W");

            try {
                this.logger.warn(MONITOR_EVENT_MARKER, toJson(monitorCode, message, context), throwable);
            } catch (Throwable var6) {
                this.selfError(monitorCode, message, context, throwable);
            }
        }

    }

    public boolean isErrorEnabled() {
        return this.logger.isErrorEnabled(MONITOR_EVENT_MARKER);
    }

    public void error(String monitorCode, String message) {
        this.error(monitorCode, message, (Map) null, (Throwable) null);
    }

    public void error(String monitorCode, String message, Throwable throwable) {
        this.error(monitorCode, message, (Map) null, throwable);
    }

    public void error(String monitorCode, String message, Map<String, String> context) {
        this.error(monitorCode, message, context, (Throwable) null);
    }

    public void error(String monitorCode, String message, Map<String, String> context, Throwable throwable) {
        if (this.isErrorEnabled()) {
            monitorCode = this.resolveMonitorCode(monitorCode, "E");
            try {
                this.logger.error(MONITOR_EVENT_MARKER, toJson(monitorCode, message, context), throwable);
            } catch (Throwable var6) {
                this.selfError(monitorCode, message, context, throwable);
            }
        }

    }

    public void logEvent(ServiceErrorCode statusCode) {
        this.logEvent(statusCode, (Map) null, (Throwable) null);
    }

    public void logEvent(ServiceErrorCode statusCode, Throwable throwable) {
        this.logEvent(statusCode, (Map) null, throwable);
    }

    public void logEvent(ServiceErrorCode statusCode, Map<String, String> context) {
        this.logEvent(statusCode, context, (Throwable) null);
    }

    public void logEvent(ServiceErrorCode statusCode, Map<String, String> context, Throwable throwable) {
        if (statusCode != null && statusCode.isClientError()) {
            this.warn(statusCode.getCode(), statusCode.getMessage(), context, throwable);
        } else if (statusCode != null && statusCode.isServerError()) {
            this.error(statusCode.getCode(), statusCode.getMessage(), context, throwable);
        } else {
            this.info(statusCode == null ? null : statusCode.getCode(), statusCode == null ? null : statusCode.getMessage(), context, throwable);
        }

    }

    private String resolveMonitorCode(String monitorCode, String prefix) {
        if (monitorCode != null && monitorCode.length() == 6) {
            String errorType = "S";
            if (monitorCode.startsWith("4")) {
                errorType = "B";
            } else if (monitorCode.startsWith("5")) {
                errorType = "S";
            } else if (monitorCode.startsWith("2")) {
                errorType = "B";
            }
            monitorCode = prefix + errorType + AppInfoUtils.getAppInfo().getAppId() + monitorCode;
        }
        return monitorCode;
    }
}
